package cz.jm.web.jmlib.client.base;

import com.google.gwt.user.client.Timer;


/**
 * Bazovy presenter pre presentre, ktore vykonavaju cinnosti pri ktorych je
 * potrebne blokovat uzivatela od ostatnych cinnosti. Prezenter dokaze vyvolat
 * zasedenie obrazovky a zobrazenie indikatoru working/loading. K tomuto ucelu
 * sluzia medtody {@link #blockApplication()} a {@link #unblockApplication()}.
 * Pokial dojde k velmi rychlemu volaniu tychto metod po sebe tak dojde k
 * odblokovaniu aplikacie az po urcenom cese {@link #MINIMAL_BLOCK_DELAY}. To
 * preto aby userovi neblikala obrazovka. Pokial presenter nechce/nemoze cakat
 * ani tento kratky cas tak je k dispozicii {@link #forceUnblockApplication()},
 * ktora odblokuje okamzite.
 * */
public abstract class BlockingPresenter extends Presenter {

	/**
	 * Stavy blokovania presenteru v ktorych sa moze nachadzat.
	 * @see #UNBLOCK
	 * @see #INIT_BLOCK
	 * @see #BLOCK
	 * @see #WAIT_UNBLOCK  
	 * */
	public enum BlockingState {
		/** Aplikacia nie je blokovana tj. normalne bezi. */
		UNBLOCK,
		/**
		 * Aplikacia je v inicialnom blokovacom stave. Teda pre usera je uz
		 * blokovana. Avsak klasicke volanie
		 * {@link BlockingPresenter#unblockApplication()} aplikaciu neodblokuje
		 * okamzite ale az po uplynuti
		 * {@link BlockingPresenter#MINIMAL_BLOCK_DELAY} (cas sa meria od
		 * zavolania {@link BlockingPresenter#blockApplication()}).
		 */
		INIT_BLOCK,
		/** Aplikacia je blokovana. */
		BLOCK,
		/**
		 * Aplikacia je blokovana avsak uz bolo poziadane o odblokovanie (pocas
		 * stavu {@link #INIT_BLOCK}) a bude automaticky odblokovana po uplynuti
		 * minimalneho casu blokovania.
		 */
		WAIT_UNBLOCK
	};

	/** Minimalna doba zablokovania aplikacie v milisekundach. */
	private final static int MINIMAL_BLOCK_DELAY = 1000; // ms

	/** Aktualny stav blokovania aplikacie. */
	private BlockingState blockingState = BlockingState.UNBLOCK;

	/**
	 * Timer straziaci minimalnu dobu blokovania. Tj. po uplynuti pozadovanej
	 * doby {@value #MINIMAL_BLOCK_DELAY} ms prepne inicialne blokovanie na
	 * klasicke blokovanie. V pripade poziadavku na odblokovanie pocas
	 * minimalnej doby blokovania sa postara aj o odblokovanie aplikacie a
	 * nastavenie korektneho stavu (neblokujuci stav).
	 */
	private final Timer initialBlockTimer = new Timer() {

		@Override
		public void run() {
			switch (blockingState) {
				case INIT_BLOCK:
					// Short block skoncil mozem sa prepnut do klasickeho
					// blokovania.
					blockingState = BlockingState.BLOCK;
					break;
				case WAIT_UNBLOCK:
					// Poziadavka na odblokovanie pocas SHORT_BLOCK bola
					// odlozena. Teraz ju realizujem.
					forceUnblockApplication();
					break;
				case UNBLOCK:
					assert false : "Pri stave UNBLOCK nema byt timer pusteny. ";
					break;
				case BLOCK:
					assert false : "Do stavu BLOCK prepina len sam timer a sam sa nespusta opakovane -> chyba.";
					break;
				default:
					assert false : "Priadana moznost ktora nie je osetrena.";

			}

		}
	};

	/**
	 * Zablokuj aplikaciu. Blokovanie je mozne len pokial je aplikcia v
	 * neblokovanom stave.
	 * 
	 * @see #getBlockingState()
	 */
	public void blockApplication() {
		assert blockingState == BlockingState.UNBLOCK : "Pokus o blokovanie aplikacie v stave roznom od UNBLOCK (state=="
		        + blockingState.toString() + ")";
		if (blockingState == BlockingState.UNBLOCK) {
			blockingState = BlockingState.INIT_BLOCK;
			initialBlockTimer.schedule(MINIMAL_BLOCK_DELAY);
			ModalLoadingPanel.getInstance().show();
		}
	}

	/**
	 * Odblokuj aplikaciu. Pokial neuplynul minimalny limit blokovania
	 * {@value #MINIMAL_BLOCK_DELAY} ms tak odlozi odblokovanie a po uplynuti
	 * doby odblokuje. V pripade ze je uz pozadovana minimalna doba blokovania
	 * uplynuta tak odblokuje priamo.
	 * 
	 * @see BlockingPresenter#blockApplication()
	 * @see #forceUnblockApplication()
	 */
	public void unblockApplication() {
		switch (blockingState) {
			case INIT_BLOCK:
				// Este neuplynul cas pre minimalne blokovanie takze budem
				// cakat.
				blockingState = BlockingState.WAIT_UNBLOCK;
				break;
			case WAIT_UNBLOCK:
				// Uz cakam na odblokovanie takze bude bez zmeny cakat dalej.
				break;
			case UNBLOCK:
				assert false : "Pokus o odblokovanie neblokovaneho stavu.";
				break;
			case BLOCK:
				// Minimalny cas blokovania uplynul takze mozem kludne
				// odblokovat.
				forceUnblockApplication();
				break;
			default:
				assert false : "Priadana moznost ktora nie je osetrena.";
		}
	}

	/**
	 * Odblokuje aplikaciu okamzite bez cakania na uplynutie minimalnej doby
	 * blokovania.
	 * 
	 * @see #unblockApplication()
	 * @see BlockingPresenter#blockApplication()
	 */
	public void forceUnblockApplication() {
		initialBlockTimer.cancel();
		blockingState = BlockingState.UNBLOCK;
		ModalLoadingPanel.getInstance().hide();
	}

	/**
	 * Vrati aktualny stav blokovania aplikacie.
	 * 
	 * @see BlockingState
	 */
	public BlockingState getBlockingState() {
		return blockingState;
	}
}
